//FILE_HEADER-----------------------------------------------------------------           
// Copyright (c) shenzhen baijie technology Co.,Ltd.           
// Company Confidential           
//----------------------------------------------------------------------------           
// FILE NAME      : laser_fpga_fx2_sl.v           
// DEPARTMENT     : Baijie R&D department           
// AUTHOR         : Tony Su           
// AUTHOR'S EMAIL : tony@szbaijie.cn/510075462@qq.com         
//----------------------------------------------------------------------------           
// RELEASE HISTORY           
// VERSION     DATE         AUTHOR       DESCRIPTION  
// 1.0                      Chenjun      Creation         
// 2.0         2017/7/19    Tony Su      Modify FX2 master FIFO to slave FIFO
//----------------------------------------------------------------------------           
// KEYWORDS :            
//----------------------------------------------------------------------------           
// PURPOSE  :            
//----------------------------------------------------------------------------           
// PARAMETERS           
//      PARAMETER NAME    RANGE  :  DESCRIPTION      :  DEFAULT    : UNITS           
//      N/A           
//----------------------------------------------------------------------------           
// REUSE ISSUES           
//   reset_n Strategy   : system reset_n, rst_n, active-low           
//   Clock Domains    : clkout_0, clkout_180
//   Critical Timing  : N/A           
//   Test Features    : N/A           
//   Asynchronous I/F : N/A           
//   Scan Methodology : N/A           
//   Instantiations   : N/A           
//   Call Block       : N/A           
//   Synthesizable    : yes           
//   Other            : N/A           
//END_HEADER------------------------------------------------------------------
`timescale 1ns/100ps

module laser_fpga_fx2_sl
#(
  parameter [7:0] CMD_SETFQ            =8'd1, 
  parameter [7:0] CMD_SETPOWER         =8'd2, 
  parameter [7:0] CMD_MOVESCANNER      =8'd3, 
  parameter [7:0] CMD_LASERON          =8'd4, 
  parameter [7:0] CMD_LASEROFF         =8'd5, 
  parameter [7:0] CMD_SETREDLIGHT      =8'd6,
  parameter [7:0] CMD_SETSHUTTER       =8'd7,
  parameter [7:0] CMD_SETMARKSTART     =8'd8, 
  parameter [7:0] CMD_SETMARKSTOP      =8'd9, 
  parameter [7:0] CMD_SETIO_OUTPUT     =8'd10, 
  parameter [7:0] CMD_SETMINDELAY      =8'd11, 
  parameter [7:0] CMD_SETSUBDELAY      =8'd12, 
  parameter [7:0] CMD_SETLASERTYPE     =8'd13,
  parameter [7:0] CMD_SETIOOUTPUTTYPE  =8'd14,  
  parameter [7:0] CMD_GETIO            =8'd51, 
  parameter [7:0] CMD_GETLASER_STATUS  =8'd52, 
  parameter [7:0] CMD_GETSYS_STATUS    =8'd53, //not yet
  parameter [7:0] CMD_GETSHUTTER_STATUS=8'd54, //not yet
  parameter [7:0] CMD_TEST_MODE_IN     =8'd255, 
  parameter [7:0] CMD_BOARD_CHECK      =8'd168, 
  parameter [7:0] CMD_TEST_MODE_OUT    =8'd254,
    
  parameter [1:0] LASER_TYPE_IPG       = 2'b00,
  parameter [1:0] LASER_TYPE_JPT       = 2'b01,
  parameter [1:0] LASER_TYPE_CO2       = 2'b10,
  parameter [1:0] LASER_TYPE_ZW        = 2'b11
)(
  input              clkout            ,
  input              reset_n           ,
  //input              fpga_wr           ,
  //input              fpga_rd           , 
  input      [5:0]   opto_in           , //opto coupler inputs
  input              flaga_n           , //USB FIFO EP2 empty flag, active-low
                                       
  inout      [7:0]   sl_fifo_data             ,  //modify pd_io to pb_io and name it "sl_fifo_data"
  
  
  output reg [1:0]   sl_fifo_adr        , //USB fifo part address. such as 00:EP2; 10:EP6
  output reg         sloe_n             , //USB slave FIFO output enable, active-low
  output reg         slrd_n             , //USB slave FIFO read enable, active-low
  output reg         slwr_n             , //USB slave fifo write enable, active-low
  
  //output             fifo_full         ,
  output reg         clkout_p          , 
  output reg         clkout_n          ,
  output reg         sync_p            ,
  output reg         sync_n            ,
  output reg         data_x_p          ,
  output reg         data_x_n          ,
  output reg         data_y_p          ,
  output reg         data_y_n          ,
  output reg [7:0]   laser_power_out   ,
  output             laser_power_latch ,
  output reg         laser_ctl0_gate   ,
  output reg         laser_ctl1_a      ,
  output reg         laser_ctl2_b      ,
  output             laser_ctl3_dir    , //reg
  output reg [5:0]   opto_out          ,
  //output reg         eint0             , // 反馈给单片机的中断，低电平有效
                                       
  output reg        led0               , //green. indicate the laser switch is on or off
  output            led1               , //red. indicate the fpga is running
  output reg        clk_reg           
);
  
  
  assign  led1 = 1'b0  ; //red. indicate the fpga is running
  assign  laser_power_latch = 1'b1;  //控制574的输出，始终为1
  assign  laser_ctl3_dir = 1'b1;
  
  reg  rst_n_d0;
  reg  rst_n_d1;
  always @ ( posedge clkout, negedge reset_n )
  begin
  	if ( reset_n == 1'b0 )
  		begin
  			rst_n_d0 <= 1'b0;
  			rst_n_d1 <= 1'b0;
  		end
  	else
  		begin
  			rst_n_d0 <= reset_n;
  			rst_n_d1 <= rst_n_d0;
  		end
  end
  
  wire clkout_0  ;
  wire clkout_90 ;
  wire clkout_180;
  wire clkout_270;
  wire rst_n     ; //system reset, active-low
  
  
  pll_system  
  pll_system_inst 
  (
  .areset ( !rst_n_d1  ),
  .inclk0 ( clkout     ),
  //outputs
  .c0     ( clkout_0   ),///48MHz 0 degree shift to clkout
  .c1     ( clkout_90  ),///192MHz for sampling
  .c2     ( clkout_180 ),///48MHz 180 degree shift to clkout
  .c3     ( clkout_270 ),///48MHz 270 degree shift to clkout
  .locked ( rst_n      ) ///pll locked, for generating reset signal
  );
  
  
  ///for signal tap II sampling clk////////
  always @ ( posedge clkout_90, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      clk_reg <= 1'b0;
    else
      clk_reg <= ~clk_reg;
  end
  ///for signal tap II sampling clk////////
  
  
  
  reg fifo_flush;
  
  always @ ( posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		fifo_flush <= 1'b1;
  	else
  		fifo_flush <= 1'b0;
  end
  
  parameter [3:0] FX2_IDLE  = 4'b0001;
  parameter [3:0] FX2_READ  = 4'b0010;
  parameter [3:0] FX2_WRITE = 4'b0100;
  
  reg [3:0] fx2_status;
  reg [3:0] wait_cnt;
  reg [3:0] wr_cnt;
  
  always @ ( posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		begin
  		  fx2_status <= FX2_IDLE;
  		  wait_cnt   <= 4'd0;
  		  wr_cnt     <= 4'd0;
  		  sloe_n     <= 1'b1;
  		  slrd_n     <= 1'b1;
  		  slwr_n     <= 1'b1;
  		  sl_fifo_adr   <= 2'b00;
  	  end
  	else
  		begin
  			case( fx2_status )
  				FX2_IDLE :
  				  begin
  		        if ( wait_cnt == 4'd2 )
  		        	begin
  		        		wait_cnt <= wait_cnt;
  		        		if ( (flaga_n == 1'b1)&&(fifo_full==1'b0) )///and also FPGA FIFO not full
  		        		//if ( flaga_n == 1'b1 )
  		        			begin
  		        			  fx2_status <= FX2_READ;
  		        			  sloe_n <= 1'b0;
  		                slrd_n <= 1'b0;
  		                slwr_n <= 1'b1;
  		                
  		                sl_fifo_adr <= 2'b00;
  		        		  end
  		        		else if ( fb_en == 1'b1 )
  		        			begin
  		        				fx2_status <= FX2_WRITE;
  		        				wr_cnt      <= 4'd0;
  		        				//data_out_en <= 1'b1;
  				  				  //data_out    <= fb_buf[7:0];
  				  				  sloe_n <= 1'b1;
  		                slrd_n <= 1'b1;
  		        				slwr_n      <= 1'b0;
  		        				sl_fifo_adr <= 2'b10;
  		        			end
  		        		else
  		        			begin
  		        			  fx2_status <= fx2_status; //IDLE
  		        			  sloe_n <= 1'b1;
  		                slrd_n <= 1'b1;
  		                slwr_n <= 1'b1;
  		                sl_fifo_adr <= sl_fifo_adr;
  		        		  end
  		        	end
  		        else
  		        	begin
  		        		wait_cnt <= wait_cnt + 1'b1;
  		        		fx2_status <= fx2_status; //IDLE
  		        	end
  				  end
  				FX2_READ :
  				  begin
  				  	wait_cnt <= 4'd0;
  		        //slwr_n <= 1'b1;
  		        //sl_fifo_adr <= 2'b00;
  		        if ( slrd_neg == 1'b1 )
  		        	begin
  		        	  fx2_status <= FX2_IDLE;
  		        	  
  		        	  sloe_n <= 1'b1;
  		            slrd_n <= 1'b1;
  		          end
  		        else
  		        	begin
  		        	  fx2_status <= fx2_status;
  		        	  sloe_n <= 1'b0;
  		            slrd_n <= 1'b0;
  		          end
  				  end
  				FX2_WRITE:
  				  begin
  				  	if ( wr_cnt == 4'd7 )
  				  		begin
  				  			fx2_status <= FX2_IDLE;
  				  			wr_cnt <= 4'd0;
  				  			slwr_n <= 1'b1;
  				  			
  				  			//data_out_en <= 1'b0;
  				  			//data_out <= 8'h00;
  				  		end
  				  	else
  				  		begin
  				  			fx2_status <= FX2_WRITE;
  				  			wr_cnt <= wr_cnt + 1'b1;
  				  			case( wr_cnt )
  				  				4'd0: slwr_n <= 1'b0;
  				  				4'd1, 4'd2: slwr_n <= 1'b1;
  				  				4'd3, 4'd4: slwr_n <= 1'b0;
  				  				4'd5, 4'd6: slwr_n <= 1'b1;
  				  				default:
  				  				  begin
  				  				  	fx2_status  <= fx2_status;
  				  				  	data_out_en <= data_out_en;
  				  				  	data_out    <= data_out;
  				  				  	slwr_n      <= slwr_n;
  				  				  end
  				  			endcase
  				  		end
  				  end
  			  default: 
  			    begin
  			    	fx2_status <= fx2_status;
  			    	sloe_n <= sloe_n;
  			    	slrd_n <= slrd_n;
  			    	slwr_n <= slwr_n;
  			    	sl_fifo_adr <= sl_fifo_adr;
  			    end
  			endcase
  		end
  end
  
  
  reg       data_out_en;
  reg [7:0] data_out;
  always @ ( posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		begin
  			data_out_en <= 1'b0;
  			data_out    <= 8'h00;
  		end
  	else if ( fb_en == 1'b1 )
  		begin
  			data_out_en <= 1'b1;
  			data_out    <= fb_buf[7:0];
  		end
  	else if ( fx2_status == FX2_WRITE )
  		case( wr_cnt )
  			4'd0, 4'd1: data_out_en <= 1'b1;
  			4'd2: data_out_en <= 1'b0;
  			4'd3:
  			  begin
  			  	data_out_en <= 1'b1;
  			  	data_out    <= fb_buf[15:8];
  			  end
  			//4'd4:
  			//4'd5:
  			4'd6:data_out_en <= 1'b0;
  			//4'd7:
  			default:     
  			  begin
  			  	data_out_en <= data_out_en;
  			  	data_out    <= data_out   ;
  			  end
  		endcase
  end
  
  
  assign sl_fifo_data = (data_out_en)? data_out:8'hzz;
 
  
  reg  slrd_n_d0;
  wire slrd_neg;
 
  assign slrd_neg = slrd_n_d0 & ~slrd_n;
  always @ ( posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		slrd_n_d0 <= 1'b1;
  	else
  		slrd_n_d0 <= slrd_n;
  end		
  
  
  reg       data_in_en;
  reg [7:0] data_in   ;
  
  always @ ( posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		begin
  			data_in_en <= 1'b0;
  			data_in    <= 8'h00;
  		end
  	else if ( slrd_neg == 1'b1 )
  		begin
  			data_in_en <= 1'b1;
  			data_in    <= sl_fifo_data; // data from USB slave FIFO
  		end
  	else
  		begin
  			data_in_en <= 1'b0;
  			data_in    <= data_in;
  		end
  end
  
  
  
  
  wire fifo_full;
  wire fifo_empty;
  wire [7:0] fifo_dout;
  wire [11:0] fifo_usedw;
    
  fifo_4096x8     
  fifo_4096x8_inst
  (
  //inputs
  .clock ( clkout_0    ), //
  .sclr  ( fifo_flush  ), //flush the fifo
  .wrreq ( data_in_en  ), //fifo write enable, active-high//应该优化为
  .data  ( data_in     ), //fifo data in
  .rdreq ( fifo_rden   ), //fifo read enable, active-high
  //outputs            
  .q     ( fifo_dout  ), 
  .full  ( fifo_full   ), 
  .empty ( fifo_empty ), 
  .usedw ( fifo_usedw )  
  );
  
  

  reg fifo_rden;
  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      fifo_rden <= 1'b0; 
    else if ( fifo_empty == 1'b1 )
      fifo_rden <= 1'b0;
    else if ( laser_set_pulse_width_flag == 1'b1 )//while JPT laser pulse setting will not read fifo
      fifo_rden <= 1'b0;
    else if ( (scanner_data_rdy == 1'b1) )
      fifo_rden <= 1'b0;
    //else if ( data_stream_timeout >= DATA_STREAM_TIMEOUT_LEVEL )
    //  fifo_rden <= 1'b0;
    //else if ( fifo_rdy == 1'b0 ) //can remove, cuz it's related withe fifo1_empty and fifo_usdw
    //  fifo_rden <= 1'b0;
    else
      fifo_rden <= 1'b1;
  end
  
 
  //FIFO data from USB: cmd and data parse
  reg [7:0] cmd;
  reg [3:0] cmd_len;
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
        cmd     <= 8'd0;
        cmd_len <= 4'd0;
      end
    else if ( fifo_rden == 1'b1 )
      begin
        if ( cmd_len == 4'd0 )
          begin
            cmd <= fifo_dout;
            case( fifo_dout )
              CMD_SETFQ          : cmd_len <= 4'd4;
              CMD_SETPOWER       : cmd_len <= 4'd4;
              CMD_MOVESCANNER    : cmd_len <= 4'd4;
              CMD_BOARD_CHECK    : cmd_len <= 4'd4;
              CMD_SETMINDELAY    : cmd_len <= 4'd4;
              CMD_SETSUBDELAY    : cmd_len <= 4'd4;
                                 
              CMD_SETREDLIGHT    : cmd_len <= 4'd1;
              CMD_SETSHUTTER     : cmd_len <= 4'd1;
              CMD_SETIO_OUTPUT   : cmd_len <= 4'd1;
              CMD_SETIOOUTPUTTYPE: cmd_len <= 4'd1;
              CMD_SETLASERTYPE   : cmd_len <= 4'd1;
              default:             cmd_len <= 4'd0;
            endcase
          end
        else
          begin
            cmd     <= cmd;
            cmd_len <= cmd_len - 1'b1;
          end
      end
    else
      begin
        cmd     <= cmd;
        cmd_len <= cmd_len;
      end
  end

  
  
  // for test mode to test reflector signal
  reg         test_mode;
  reg [26:0]  test_cnt;
  reg [21:0]  io_signal_map;
  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
        test_mode     <= 1'b0;
        test_cnt      <= 27'd0; 
        io_signal_map <= 21'd0;
      end
    else if ((fifo_rden == 1'b1)&&(cmd_len == 4'd0 ))
      begin
        case( fifo_dout )
          CMD_TEST_MODE_IN :
            begin
              test_mode     <= 1'b1;
              io_signal_map <= ~21'b1; //21'b111111111111111111110;
            end
          CMD_TEST_MODE_OUT: test_mode  <= 1'b0;
          default:
            begin
              test_mode     <= test_mode;
              io_signal_map <= io_signal_map;
            end
        endcase
      end
    else if ( test_mode == 1'b1 )
      begin
        if ( test_cnt >= 27'd6000000 ) // 125ms
          begin
            test_cnt  <= 27'd0;
            io_signal_map <= {io_signal_map[20:0], io_signal_map[21]}; 
          end
        else
          begin
            test_cnt  <= test_cnt + 1'b1;
            io_signal_map <= io_signal_map;
          end
      end
    else
      begin
        test_mode <= 1'b0;
        test_cnt  <= 27'd0; 
        io_signal_map <= 21'd0;
      end
  end
  

  reg mark_start;  // 0: mark stoped, 1: mark started
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n ==1'b0 )
      mark_start <= 1'b0;
    else if ( (fifo_rden == 1'b1)&&(cmd_len == 4'd0) )
      begin
        case( fifo_dout )
          CMD_SETMARKSTART: mark_start <= 1'b1;
          CMD_SETMARKSTOP : mark_start <= 1'b0;
          default:         mark_start <= mark_start;
        endcase
      end
    else if( data_stream_timeout >= DATA_STREAM_TIMEOUT_LEVEL )
      mark_start <= 1'b0;
    else
      mark_start <= mark_start;
  end
  
  
  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      opto_out <= 6'h00;
    else if ( test_mode == 1'b1 )
      opto_out <= io_signal_map[13:8];
    else if ( pulse_width_bit_cnt == 7'd68 ) //JPT laser pulse width set over    //
      opto_out[2] <= 1'b0;   // PA <= 0;// 
    else if ( fifo_rden == 1'b1 )
      begin
        if ( cmd_len == 4'd0 )
          begin
            case( fifo_dout )
              CMD_SETMARKSTART: opto_out[0] <= !io_output_type[0];    //判断该位是否反向
              CMD_SETMARKSTOP : opto_out[0] <=  io_output_type[0];
              default:          opto_out[0] <= opto_out[0] ;
            endcase
          end
        else if ( cmd_len == 4'd1 )
          begin
            case( cmd )
              CMD_SETFQ :
                begin
                  if ( laser_type == LASER_TYPE_JPT )//JPT laser pulse width set by serial digital signal
                    opto_out[2] <= 1'b1; // Open the PA，Pin22 of DB25, which is OUT_2
                  else
                    opto_out[2] <= opto_out[2];
                end
              CMD_SETREDLIGHT : opto_out[2] <= fifo_dout[0];
              CMD_SETIO_OUTPUT: opto_out    <= fifo_dout[5:0];
              default: opto_out <= opto_out;
            endcase
          end
        else
          begin
            opto_out <= opto_out;
          end
      end
    else
      opto_out <= opto_out;
  end
  
  
  ///laser gate control//////////////////////////////////////////////////  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      laser_ctl0_gate <= 1'b1; //1:turn off the laser; 0: turn on the laser. the signal will reverse by opto
    else if( test_mode == 1'b1 )
      laser_ctl0_gate <= !io_signal_map[16];
    else if ( (fifo_rden == 1'b1)&&( cmd_len == 4'd0 ) )
      begin
        case( fifo_dout )
          CMD_LASERON  : laser_ctl0_gate <= 1'b0;
          CMD_LASEROFF : laser_ctl0_gate <= 1'b1;
          default: laser_ctl0_gate <= laser_ctl0_gate ;
        endcase
      end
    else if ( data_stream_timeout >= DATA_STREAM_TIMEOUT_LEVEL ) //100ms                     //
      laser_ctl0_gate <= 1'b1;  //long time no data in FIFO,turn off the laser in case of the laser keeping on  //
    else
      laser_ctl0_gate <= laser_ctl0_gate;
  end
  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      led0 <= 1'b1; //green led,active-low. indecate the laser status
    else
      led0 <= laser_ctl0_gate ;
  end
  ///laser gate control//////////////////////////////////////////////////
  
  
  reg [23:0]  data_stream_timeout;
  always @ ( posedge clkout_180, negedge rst_n ) 
  begin
    if ( rst_n == 1'b0 )
      data_stream_timeout <= 24'd0;
    else if ( (fifo_rden == 1'b1))//||( fifo1_usedw > 0 ) )
      data_stream_timeout <= 24'd0;
    else if ( data_stream_timeout >= DATA_STREAM_TIMEOUT_LEVEL )
      data_stream_timeout <= data_stream_timeout;
    else
      data_stream_timeout <= data_stream_timeout + 1'b1;
  end
  
  
  
  reg [1:0] laser_type;
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      laser_type <= LASER_TYPE_IPG;
    else if ( (fifo_rden == 1'b1)&&(cmd_len == 4'd1) )
      begin
        if ( cmd == CMD_SETLASERTYPE )
          begin
            case( fifo_dout )
              2'b00: laser_type <= LASER_TYPE_IPG;
              2'b01: laser_type <= LASER_TYPE_JPT;
              2'b10: laser_type <= LASER_TYPE_CO2;
              2'b11: laser_type <= LASER_TYPE_ZW;
              default:laser_type <= laser_type;
            endcase
          end
        else
          laser_type <= laser_type;
      end
    else
      laser_type <= laser_type;
  end
  
  
  reg [7:0] io_output_type;
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      io_output_type <= 8'h00;
    else if ( (fifo_rden == 1'b1)&&( cmd_len == 4'd1 ) )//( (cmd_len == 4'd1)&&( cmd == CMD_SETIOOUTPUTTYPE ) )
      if ( cmd == CMD_SETIOOUTPUTTYPE )
        io_output_type <= fifo_dout; //[0]:MO ctrl; all the output will reversed by opto
      else
        io_output_type <= io_output_type;
    else
      io_output_type <= io_output_type;
  end
  
  

  
  
  reg [31:0]  board_check_magic;
  reg         bd_check_ok;
  ///If not the OK board, will not begin scanner moving
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
        board_check_magic <= 32'd0;
        bd_check_ok       <= 1'b0 ;
      end
    else if ( (fifo_rden == 1'b1)&&( cmd == CMD_BOARD_CHECK ) )
      case( cmd_len )

        4'd4, 4'd3, 4'd2: board_check_magic <= { fifo_dout, board_check_magic[31:8] };
        4'd1:
          begin
            board_check_magic <= { fifo_dout, board_check_magic[31:8] };
            if ( { fifo_dout, board_check_magic[31:8] } == 32'd77168 )//32'h0001_2D70;
              bd_check_ok <= 1'b1; //OK
            else
              bd_check_ok <= 1'b0; //not OK
          end
        default:
          begin
            board_check_magic <= board_check_magic;
            bd_check_ok       <= bd_check_ok;
          end
      endcase  
    else
      begin
        board_check_magic <= board_check_magic;
        bd_check_ok       <= bd_check_ok;
      end
  end
  
  
  
  reg [31:0]  laser_freq;
  reg [31:0]  laser_freq_save;
  reg         laser_freq_ready;
  reg [31:0]  laser_power;
  
  reg        laser_set_pulse_width_flag;
  reg [18:0] pulse_width_time_cnt; // 用于计算发送脉宽的时间
  reg [31:0] pulse_width_tmp;  // 保存脉宽临时数据
  reg [ 6:0] pulse_width_bit_cnt;  //对于JPT激光器，记录已发送的脉宽数据位数
  
  parameter DATA_STREAM_TIMEOUT_LEVEL = 9600000;  //设为100ms没有数据则停止出光

  
  //读FIFO，并处理命令，并准备好数据给后级振镜处理，等后级需要数据时，再次准备数据
  //clkout的下降沿处理，正好避开了上升沿的时候数据变化的时间点，所以当empty=1时，
  //设置rden=0不会出错
  
  
   //always @ ( posedge clkout_180, negedge rst_n )
  always @ ( negedge rst_n or posedge clkout_180 ) begin
    if ( rst_n == 1'b0 ) begin
      laser_set_pulse_width_flag <=  1'b0;
      laser_freq                 <= 32'd0;
      laser_freq_save            <= 32'd0;
      laser_freq_ready           <=  1'b0;
      laser_power                <= 32'd0;
      laser_power_out            <= 8'hff;
    end
    else if(test_mode == 1'b1) 
      begin
        laser_power_out <= {io_signal_map[3:0], io_signal_map[20:17]};
      end
//    else if(laser_set_pulse_width_flag == 1)    // deticated to JPT laser
//    begin
//      pulse_width_time_cnt <= pulse_width_time_cnt + 1'b1;
//      if(pulse_width_time_cnt == 24*100*pulse_width_bit_cnt)  // 100us 1 bit, pulse_width_time_cnt最大值=32x2
//      begin
//        pulse_width_bit_cnt <= pulse_width_bit_cnt+1'b1;
//        if(pulse_width_bit_cnt < 4'h4)    //等待PA 4个位周期，因为光耦速度慢
//        begin
//        end
//        else if(pulse_width_bit_cnt[0] == 1'b1)
//        begin
//          laser_power_out[2] <= 1;    // clk
//        end
//        else if(pulse_width_bit_cnt == (64+4))
//        begin
//          laser_power_out[2] <= 0;
//          laser_set_pulse_width_flag <= 0;
//        end
//        else begin
//          laser_power_out[1] <= pulse_width_tmp[31];
//          laser_power_out[2] <= 0;
//          pulse_width_tmp <= {pulse_width_tmp[30:0], 1'b0};
//        end
//      end
//    end
    else if( laser_set_pulse_width_flag == 1'b1 )  //deticated to JPT laser freq setting
      begin
        if ( pulse_width_time_cnt == 19'd4799 )  // 100us 1 bit
          begin
            pulse_width_time_cnt <= 19'd0;
            if ( pulse_width_bit_cnt == 7'd33 )
              begin
                pulse_width_bit_cnt <= 7'd0;
                laser_set_pulse_width_flag <= 1'b0; //JPT freq and pulse width setting is over
                
                pulse_width_tmp    <= 32'd0;
                laser_power_out[1] <=  laser_power[1]; //recover the original power value after setting
                laser_power_out[2] <=  laser_power[2]; //recover the original power value after setting
              end
            else if ( (pulse_width_bit_cnt >= 7'd1)&&( pulse_width_bit_cnt <= 7'd32 ) )
              begin//wait for two bits time, as the opto response not so fast
                pulse_width_bit_cnt <= pulse_width_bit_cnt + 1'b1;
                laser_set_pulse_width_flag <= 1'b1;
                
                pulse_width_tmp   <= {pulse_width_tmp[30:0], 1'b0};
                laser_power_out[1] <= pulse_width_tmp[31]; ///JPT pulse width setting:Serial data
                laser_power_out[2] <= 1'b1; //SCLK:serial clock.JPT pulse width setting.
              end
            else
              begin
                pulse_width_bit_cnt <= pulse_width_bit_cnt + 1'b1;
                laser_set_pulse_width_flag <= 1'b1;
                
                pulse_width_tmp    <= pulse_width_tmp ;
                laser_power_out[1] <= 1'b0;
                laser_power_out[2] <= 1'b1; //or laser_power_out[2] <= laser_power_out[2]/laser_power[2]
              end
          end
        else if ( pulse_width_time_cnt == 19'd2399 )
          begin
            pulse_width_time_cnt <= pulse_width_time_cnt + 1'b1;
            pulse_width_bit_cnt  <= pulse_width_bit_cnt;
            laser_set_pulse_width_flag <= 1'b1;
            if ( ( pulse_width_bit_cnt >= 7'd2 )&&( pulse_width_bit_cnt <= 7'd33 ) ) 
              begin//wait for two bits(SCLK) time, as the opto response not so fast
                laser_power_out[2] <= 1'b0; //JPT laser SCLK
              end
            else
              begin
                laser_power_out[2] <= 1'b1; //JPT laser SCLK
              end
          end
        else
          begin
            pulse_width_time_cnt <= pulse_width_time_cnt + 1'b1;
            pulse_width_bit_cnt  <= pulse_width_bit_cnt;
            laser_set_pulse_width_flag <= 1'b1;
            laser_power_out[2]   <= laser_power_out[2];
            
            pulse_width_tmp    <= pulse_width_tmp   ;
            laser_power_out[1] <= laser_power_out[1];
          end
      end
    else if(fifo_rden == 1'b1) 
      begin
        case(cmd)
          CMD_SETFQ:
            begin
              case( cmd_len )
                4'd4, 4'd3, 4'd2:laser_freq <= {fifo_dout, laser_freq[31:8]};
                4'h1:
                  begin
                    laser_freq_ready <= 1;
                    if(laser_type == LASER_TYPE_JPT) //JPT laser pulse set by  serial digital
                      begin
                        laser_set_pulse_width_flag <= 1'b1; //detecated to JPT laser
                        laser_freq_save <= {laser_freq[23:8]*5'd25, laser_freq[23:8]}; //频率是以微秒为单位，脉宽是以纳秒为单位，JPT激光器，取频率信息来算脉宽，频率信号一半占空比
                        pulse_width_tmp <= {8'ha5, 8'h01, fifo_dout, laser_freq[31:24]}; // 因为本次laser_freq还没有右移8位，另外，协议定义的最小单位为1ns
                        pulse_width_time_cnt <= 19'd0; //for JPT 10us counter
                        pulse_width_bit_cnt  <=  7'd0; //for JPT sending bits counter
                        laser_power_out[1] <= 1'b0;
                        laser_power_out[2] <= 1'b0;
                      end
                    else
                      begin
                        laser_freq_save <= {fifo_dout, laser_freq[31:8]};
                      end
                  end
                default: laser_freq_ready <= laser_freq_ready;
              endcase
            end
          CMD_SETPOWER: 
            begin
              case( cmd_len )
                4'd4:
                  begin
                    laser_power_out <= fifo_dout;  // only first byte
                    laser_power     <= {fifo_dout, laser_power[31:8]};
                  end
                4'd3, 4'd2, 4'd1: laser_power <= {fifo_dout, laser_power[31:8]};
                default:
                  begin
                    laser_power_out <= laser_power_out;
                    laser_power     <= laser_power ;
                  end
              endcase
            end
          default:;
        endcase
      end
    else if(data_stream_timeout >= DATA_STREAM_TIMEOUT_LEVEL)   
      laser_freq_ready <= 1'b0;
    else
    	laser_freq_ready <= laser_freq_ready;
  end

  // laser control and frequency output
  reg   [15:0]  laser_freq_cnt; //
  reg   [15:0]  laser_freq_cycle_cnt; //one cycle counter. laser freq control.
  reg   [15:0]  laser_freq_pulse_width; //pulse width counter. laser freq  control
  
  always @ (posedge clkout_0, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
      	laser_freq_cycle_cnt <= 16'd0;
      	laser_freq_pulse_width <= 16'd0;
      end
    else if ( laser_freq_ready == 1'b1 )
      begin
      	laser_freq_cycle_cnt <= { laser_freq_save[13:0], 2'b00 } + laser_freq_save[15:0];//laser_freq_save[15:0]*5
      	laser_freq_pulse_width <= laser_freq_save[31:16];
      end
    else
      begin
        laser_freq_cycle_cnt <= laser_freq_cycle_cnt;
        laser_freq_pulse_width <= laser_freq_pulse_width;
      end
  end
  
  always @ (posedge clkout_0, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
      	laser_freq_cnt <= 16'd0;
        laser_ctl1_a <= 1'b1; //disable the laser pulse output, it will reverse by opto
        laser_ctl2_b <= 1'b0; //
      end
    else if ( test_mode == 1'b1 )
      begin
        laser_ctl1_a  <= !io_signal_map[15];
        laser_ctl2_b  <= !io_signal_map[14];
      end
    else if ( laser_freq_ready == 1'b1 )
      begin
        if ( laser_freq_cnt == laser_freq_cycle_cnt )
        	begin
        		laser_freq_cnt <= 16'd0;
        		laser_ctl1_a   <= 1'b0 ; //
        		laser_ctl2_b   <= 1'b1 ;
        	end
        else if ( laser_freq_cnt == laser_freq_pulse_width )
        	begin
        		laser_freq_cnt <= laser_freq_cnt + 1'b1;
        		laser_ctl1_a   <= 1'b1;
        		laser_ctl2_b   <= 1'b0;
        	end
        else
        	begin
        		laser_freq_cnt <= laser_freq_cnt + 1'b1;
        		laser_ctl1_a   <= laser_ctl1_a;
        		laser_ctl2_b   <= laser_ctl2_b;
        	end
      end
    else
      begin
        laser_freq_cnt <= 16'd0;
  			laser_ctl1_a   <= 1'b1 ;
  			laser_ctl2_b   <= 1'b0 ;
      end
  end
  
  
  
  parameter SCANNER_DATA_TYPE_MOVING   = 1'b0;
  parameter SCANNER_DATA_TYPE_SUBDELAY = 1'b1;
  
  reg [15:0]  data_x_out; //for scanner X axis moving data
  reg [15:0]  data_y_out; //for scanner X axis moving data
  
  reg [31:0] scanner_sub_delay; //the sub_delay between the instructs(us)
  
  reg scanner_data_type; //0:moving; 1:sub_delay
  reg scanner_data_rdy; // 1:there is data in the buffer; 0: no data in the buffer
  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
        data_x_out <= 16'd0;
        data_y_out <= 16'd0;
        scanner_sub_delay <= 32'd0;
        scanner_data_rdy <= 1'b0;// means scanner data is not ready
        scanner_data_type <= 1'b0;
      end
    else if((scanner_data_read_over == 1'b0) && (scanner_data_read_over_d0 == 1'b1) && (scanner_data_rdy == 1'b1)) begin // (scanner_data_read_over下降沿的时候读数据
      scanner_data_rdy <= 1'b0;
    end
    else if ( (fifo_rden == 1'b1))
      begin
        case( cmd )
          CMD_MOVESCANNER :
            begin
              case( cmd_len )
                4'd4: data_x_out[ 7:0] <= fifo_dout; 
                4'd3: data_x_out[15:8] <= fifo_dout;
                4'd2: data_y_out[ 7:0] <= fifo_dout; 
                4'd1: 
                  begin
`ifdef  BD_CHECK
                    if(bd_check_ok ==1'b1) //if board check is not ok, the scanner will not move
`endif        
                      begin
                        data_y_out[15:8] <= fifo_dout;
                        scanner_data_rdy <= 1'b1;
                        scanner_data_type <= SCANNER_DATA_TYPE_MOVING;
                      end
                  end
                default:scanner_data_rdy <= 1'b0;
              endcase
            end
          CMD_SETMINDELAY :
            begin
              case ( cmd_len )
                4'd4: scanner_sub_delay[7:0] <= fifo_dout;
                4'd3: scanner_sub_delay[15:8] <= fifo_dout;
                4'd2: scanner_sub_delay[23:16] <= fifo_dout;
                4'd1: 
                  begin
                    scanner_sub_delay[31:24] <= fifo_dout;
                    scanner_data_rdy <= 1'b1;
                    scanner_data_type <= SCANNER_DATA_TYPE_SUBDELAY;
                  end
                default: scanner_data_rdy <= 1'b0;
              endcase
            end
          default:;
        endcase
      end
    else
      begin
        data_x_out <= data_x_out;
        data_y_out <= data_y_out;
        scanner_sub_delay <= scanner_sub_delay;
        scanner_data_rdy <= scanner_data_rdy;
        scanner_data_type <= scanner_data_type;
      end
  end
  
  
  
  
  
  reg scanner_data_read_over;
  reg scanner_data_read_over_d0;              // 表示数据已经读走,只在一个振镜位周期有效，一个位周期之后，会自动清零
  
  //scanner output control: the bus protocol is XY2-100:CLK is 2M
  reg [ 3:0] count_250ns;              // 作为500ns脉冲输出标准计数
  reg [19:0] data_x;
  reg [19:0] data_y;
  reg [ 4:0] bit20_cnt;

  
  //12 clk change one time
  always @ ( posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		begin
  			clkout_p <= 1'b1;
        clkout_n <= 1'b0;
        count_250ns <= 4'd0;
  		end
  	else if ( test_mode == 1'b1 )
  		clkout_n <= !io_signal_map[7];
  	else if ( count_250ns == 4'd11 )
  		begin
  			count_250ns <= 4'd0;
  			clkout_p <= ~clkout_p;
  			clkout_n <= ~clkout_n;
  		end
  	else
  		begin
  			count_250ns <= count_250ns + 1'b1;
  			clkout_p    <= clkout_p;
  			clkout_n    <= clkout_n;
  		end
  end



  parameter [ 3:0] SCANNER_IDLE      = 4'b0001;
  parameter [ 3:0] SCANNER_MOVING    = 4'b0010;
  parameter [ 3:0] SCANNER_MIN_DELAY = 4'b0100;
  parameter [ 3:0] SCANNER_SUB_DELAY = 4'b1000;
  
  
  
  
  reg  clkout_p_d0;
  wire clkout_p_pos = ~clkout_p_d0 & clkout_p;
  
  always @(posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
 			clkout_p_d0 <= 0;
 		else
 			clkout_p_d0 <= clkout_p;
 	end
  			
  
  
  reg [ 3:0] scanner_status;
  always @(posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		scanner_status <= SCANNER_IDLE;
  	else if ( clkout_p_pos == 1'b1 )
  		begin
  			case( scanner_status )
  				SCANNER_IDLE      :
  				  begin
  				  	if ( scanner_data_rdy == 1'b1 ) //moving and sub_delay together
  				  		scanner_status <= SCANNER_MOVING;
  				  	else
  				  		scanner_status <= scanner_status;
  				  end
  				SCANNER_MOVING    :
  				  begin
  				  	if ( bit20_cnt == 19 )
  				  		begin
  				  			if ( scanner_min_delay_flag == 1'b1 )
  				  				scanner_status <= SCANNER_MIN_DELAY;
  				  			else
  				  				scanner_status <= SCANNER_IDLE;
  				  		end
  				  	else
  				  		scanner_status <= scanner_status;
  				  end
  				SCANNER_MIN_DELAY :
  				  begin
  				  	if ( scanner_min_delay_save_flag == 1'b1 )
  				  		scanner_status <= scanner_status;
  				  	else
  				  		scanner_status <= SCANNER_IDLE;
  				  end
  				SCANNER_SUB_DELAY :
  				  begin
  				  	if ( scanner_sub_delay_save_flag == 1'b1 )
  				  		scanner_status <= scanner_status;
  				  	else
  				  		scanner_status <= SCANNER_IDLE;
  				  end
  				default: scanner_status <= scanner_status;
  			endcase
  		end
  	else
  		begin
  			scanner_status <= scanner_status;
  		end
  end
  
  
  
  always @(posedge clkout_0, negedge rst_n )
  begin
    if( rst_n == 1'b0 ) 
    	begin
        scanner_data_read_over <= 1'b0;
        data_x <= {3'b001, 16'hffff, 1'b1};
        data_y <= {3'b001, 16'hffff, 1'b1};
        sync_p <= 1;
        sync_n <= 0;
        bit20_cnt <= 5'd0;
      end
    else if(test_mode == 1'b1) 
    	begin
        sync_n   <= io_signal_map[6];
        data_x_p <= io_signal_map[5];
        data_y_p <= io_signal_map[4];
      end
    else if ( clkout_p_pos == 1'b1 )
    	begin
        if (scanner_status == SCANNER_IDLE) 
        	begin
            if((scanner_data_rdy == 1'b1) )
            	begin
                scanner_data_read_over <= 1'b1;
                bit20_cnt = 5'd1;
                data_x[19:1] <=  { 3'b001, data_x_out };
                data_x[0]    <= ^{ 3'b001, data_x_out };
                data_y[19:1] <=  { 3'b001, data_y_out };
                data_y[0]    <= ^{ 3'b001, data_y_out };
                data_x_p <= 1'b0;
                data_y_p <= 1'b0;
                sync_p   <= 1'b1;
                sync_n   <= 1'b0;
              end
            else
              begin
                scanner_data_read_over <= 1'b0;
                bit20_cnt = 5'd1;
                data_x_p <= 1'b0;
                data_y_p <= 1'b0;
                sync_p   <= 1'b1;
                sync_n   <= 1'b0;
              end
          end
        else 
        	begin
            if (scanner_status == SCANNER_MOVING) 
          	  begin
                if(bit20_cnt == 19) 
                	begin
                		bit20_cnt <= 5'd0;
                    sync_p <= 1'b0 ;
                    sync_n <= 1'b1;
                  end
                scanner_data_read_over <= 1'b0;
                data_x_p <=  data_x[19-bit20_cnt];
                data_x_n <= ~data_x[19-bit20_cnt];
                
                data_y_p <=  data_y[19-bit20_cnt];
                data_y_n <= ~data_y[19-bit20_cnt];
                bit20_cnt <= bit20_cnt+1'b1;
              end
            else if(scanner_status == SCANNER_SUB_DELAY) 
              scanner_sub_delay_save <= scanner_sub_delay_save - 1;
          end
      end
  end 
  
  
  
  
  
  reg [31:0]  scanner_min_delay; //the min_delay between intructs(us)
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      scanner_min_delay <= 32'd0;
    else if ( (fifo_rden == 1'b1)&&( cmd == CMD_SETMINDELAY ) )
      begin
        case( cmd_len )
          4'd4: scanner_min_delay[ 7: 0] <= fifo_dout;
          4'd3: scanner_min_delay[15: 8] <= fifo_dout;
          4'd2: scanner_min_delay[23:16] <= fifo_dout;
          4'd1: scanner_min_delay[31:24] <= fifo_dout;
          default: scanner_min_delay <= 32'd0;
        endcase
      end
    else
      scanner_min_delay <= scanner_min_delay;
  end
  
  reg [31:0] scanner_min_delay_save;
  
  always @(posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		scanner_min_delay_save <= 32'd0;
  	else if ( clkout_p_pos == 1'b1 )
  		begin
  			if ( scanner_status == SCANNER_MOVING )
  				begin
  					if( (bit20_cnt == 5'd19)&&( scanner_min_delay_flag == 1'b1 ))
  						scanner_min_delay_save <= (scanner_min_delay - 1)*2;  // 在送完最后一段数据之后，读取当前的最小延时，并转化为以250ns为单位的计数
  					else
  						scanner_min_delay_save <= 32'd0;
  				end
  			else if ( scanner_status == SCANNER_MIN_DELAY )
  				scanner_min_delay_save <= scanner_min_delay_save - 1;
  			else
  				scanner_min_delay_save <= 32'd0;//scanner_min_delay_save <= scanner_min_delay_save;
  		end
  	else
 			scanner_min_delay_save <= scanner_min_delay_save;
  end
  
  
  reg [31:0] scanner_sub_delay_save;
  reg        scanner_min_delay_flag;
  reg        scanner_sub_delay_flag;
  reg        scanner_min_delay_save_flag;
  reg        scanner_sub_delay_save_flag;
  
  always @(posedge clkout_0, negedge rst_n )
  begin
  	if ( rst_n == 1'b0 )
  		begin
        scanner_min_delay_flag <= 0;
        scanner_sub_delay_flag <= 0;
        scanner_min_delay_save_flag <= 0;
        scanner_sub_delay_save_flag <= 0;
  		end
  	else
  		begin 
        scanner_data_read_over_d0 <= scanner_data_read_over;                    
        scanner_sub_delay_flag <= (scanner_sub_delay > 1 ) ? 1'b1:1'b0;         
        scanner_min_delay_flag <= (scanner_min_delay >1 ) ? 1'b1:1'b0;          
        scanner_sub_delay_save_flag <= (scanner_sub_delay_save >1 ) ? 1'b1:1'b0; //never deal with it
        scanner_min_delay_save_flag <= (scanner_min_delay_save >1 ) ? 1'b1:1'b0; 
  		end
  end
  
  
  reg        fb_en;
  reg [15:0] fb_buf;  //feedback two bytes
  
  always @ ( posedge clkout_180, negedge rst_n )
  begin
    if ( rst_n == 1'b0 )
      begin
      	fb_en <= 1'b0;
        fb_buf  <= 16'h0000;
      end
    else if ( (fifo_rden == 1'b1)&&( cmd_len == 4'd0 ) ) // when cmd_len is 0, it means the first byte is command
      begin
        case( fifo_dout )//execute the command directly
          CMD_SETMARKSTOP:
            begin
            	fb_en <= 1'b1;
              fb_buf[7:0] <= CMD_SETMARKSTOP;
              fb_buf[15:8] <= 8'h00;
            end
          CMD_GETIO :
            begin
            	fb_en <= 1'b1;
              fb_buf[7:0]  <= CMD_GETIO;
              fb_buf[15:8] <= {2'b00, opto_in};
            end
          CMD_GETLASER_STATUS:
            begin
            	fb_en <= 1'b1;
              fb_buf[7:0] <= CMD_GETLASER_STATUS;
              fb_buf[15:8] <= {7'h00, ~laser_ctl0_gate};    //laser control signal will reverse by opto
            end
          default:
            begin
            	fb_en <= fb_en;
              fb_buf  <= fb_buf;
            end
        endcase
      end
    else
      begin
      	fb_en <= 1'b0;
        fb_buf  <= fb_buf;
      end
  end
  

  
  


endmodule
